package com.cccyy.financial_calculations;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 数学计算，金融相关数据计算
 */
public class MathCalculation {
    public static void main(String[] args) {
        testSqrt();
        testPow();

        //净值的计算
        FundNetValueCalculator calculator = new FundNetValueCalculator();
        calculator.calculateNetValue(1000.09d, 30.31d, 1000.00d);

        netWorthGrowthCalculator(1000.09d, 1150.31d);
        //负增长
        netWorthGrowthCalculator(1300.09d, 1150.31d);

        // 计算最大回撤
        List<Double> portfolioReturns = Arrays.asList(0.05, 0.03, 0.04, 0.02, 0.06);
        double maxDrawdown = calculateMaxDrawdown(portfolioReturns);
        System.out.printf("最大回撤为: %.2f%%\n", maxDrawdown * 100);

        // 计算收益回撤比
        double initialNetWorth = 1000.09d;
        double finalNetWorth = 1150.31d;
        double rdr = calculateReturnToDrawdownRatio(initialNetWorth, finalNetWorth, portfolioReturns);
        System.out.printf("收益回撤比为: %.2f\n", rdr);

        // 计算盈利概率 tradeResults->交易结果列表，正数表示盈利，负数表示亏损
        List<Double> tradeResults = Arrays.asList(100.0, -50.0, 200.0, -100.0, 150.0, -20.0);
        double profitProbability = calculateProfitProbability(tradeResults);
        System.out.printf("盈利概率为: %.2f%%\n", profitProbability * 100);

        // 计算收益率在一定区间 [x, y) 内的占比
        double x = 0.02;
        double y = 0.05;
        double returnRangePercentage = calculateReturnRangePercentage(portfolioReturns, x, y);
        System.out.printf("收益率在区间 [%.2f, %.2f) 内的占比为: %.2f%%\n", x, y, returnRangePercentage * 100);

        // 计算平均涨幅
        List<Double> netValues = Arrays.asList(1000.0, 1050.0, 1100.0, 1080.0, 1150.0);
        double averageGrowth = calculateAverageGrowth(netValues);
        System.out.printf("平均涨幅为: %.2f%%\n", averageGrowth);

        // 计算业绩比较基准每日基点
        List<Double> benchmarkNetValues = Arrays.asList(1000.0, 1020.0, 1030.0, 1010.0, 1040.0);
        List<Double> portfolioNetValues = Arrays.asList(1000.0, 1060.0, 1070.0, 1050.0, 1100.0);
        List<Double> basisPoints = calculateBenchmarkDailyBasisPoints(benchmarkNetValues, portfolioNetValues);
        System.out.println("业绩比较基准每日基点为: " + basisPoints);

        // 计算动态回撤
        List<Double> dynamicDrawdowns = calculateDynamicDrawdown(netValues);
        System.out.println("动态回撤为: " + dynamicDrawdowns);

    }

    /**
     * 计算动态回撤
     *
     * @param netValues 净值列表，假设是一个时间序列的净值数据
     * @return 动态回撤列表
     * @throws IllegalArgumentException 如果净值列表为空或包含的净值少于两个，则抛出此异常
     */
    public static List<Double> calculateDynamicDrawdown(List<Double> netValues) {
        if (netValues == null || netValues.size() < 2) {
            throw new IllegalArgumentException("Net value list must contain at least two values.");
        }

        List<Double> dynamicDrawdowns = new ArrayList<>();
        double peak = netValues.get(0);

        for (double netValue : netValues) {
            // 更新峰值
            if (netValue > peak) {
                peak = netValue;
            }

            // 计算当前回撤
            double drawdown = (peak - netValue) / peak;
            dynamicDrawdowns.add(drawdown);
        }

        return dynamicDrawdowns;
    }


    /**
     * 计算业绩比较基准每日基点
     *
     * @param benchmarkNetValues 基准净值列表
     * @param portfolioNetValues 投资组合净值列表
     * @return 业绩比较基准每日基点列表
     * @throws IllegalArgumentException 如果净值列表为空或包含的净值少于两个，则抛出此异常
     */
    public static List<Double> calculateBenchmarkDailyBasisPoints(List<Double> benchmarkNetValues, List<Double> portfolioNetValues) {
        if (benchmarkNetValues == null || benchmarkNetValues.size() < 2) {
            throw new IllegalArgumentException("基准净值列表必须包含至少两个值。");
        }
        if (portfolioNetValues == null || portfolioNetValues.size() < 2) {
            throw new IllegalArgumentException("投资组合净值列表必须包含至少两个值。");
        }
        if (benchmarkNetValues.size() != portfolioNetValues.size()) {
            throw new IllegalArgumentException("基准净值列表和投资组合净值列表的长度必须相同。");
        }

        int count = benchmarkNetValues.size() - 1;
        List<Double> basisPoints = new ArrayList<>();

        for (int i = 0; i < count; i++) {
            double basisPoint = getBasisPoint(benchmarkNetValues, portfolioNetValues, i);
            basisPoints.add(basisPoint);
        }

        return basisPoints;
    }

    private static double getBasisPoint(List<Double> benchmarkNetValues, List<Double> portfolioNetValues, int i) {
        double benchmarkInitial = benchmarkNetValues.get(i);
        double benchmarkCurrent = benchmarkNetValues.get(i + 1);
        double portfolioInitial = portfolioNetValues.get(i);
        double portfolioCurrent = portfolioNetValues.get(i + 1);

        if (benchmarkInitial == 0) {
            throw new IllegalArgumentException("基准净值不能为0");
        }
        if (portfolioInitial == 0) {
            throw new IllegalArgumentException("投资组合净值不能为0");
        }

        double benchmarkGrowth = ((benchmarkCurrent - benchmarkInitial) / benchmarkInitial) * 100;
        double portfolioGrowth = ((portfolioCurrent - portfolioInitial) / portfolioInitial) * 100;

        return (portfolioGrowth - benchmarkGrowth) * 100;
    }


    /**
     * 计算平均涨幅
     *
     * @param netValues 净值列表，假设是一个时间序列的净值数据
     * @return 平均涨幅
     * @throws IllegalArgumentException 如果净值列表为空或包含的净值少于两个，则抛出此异常
     */
    public static double calculateAverageGrowth(List<Double> netValues) {
        if (netValues == null || netValues.size() < 2) {
            throw new IllegalArgumentException("Net value list must contain at least two values.");
        }

        double totalGrowth = 0.0;
        int count = netValues.size() - 1;

        for (int i = 0; i < count; i++) {
            double initialNetWorth = netValues.get(i);
            double currentNetWorth = netValues.get(i + 1);
            if (initialNetWorth == 0) {
                throw new IllegalArgumentException("旧净值不能为0");
            }
            double growth = ((currentNetWorth - initialNetWorth) / initialNetWorth) * 100;
            totalGrowth += growth;
        }

        return totalGrowth / count;
    }


    /**
     * 计算收益率在一定区间 [x, y) 内的占比
     *
     * @param returns 收益率列表
     * @param x       区间下限（包含）
     * @param y       区间上限（不包含）
     * @return 收益率在区间 [x, y) 内的占比
     * @throws IllegalArgumentException 如果收益率列表为空，则抛出此异常
     */
    public static double calculateReturnRangePercentage(List<Double> returns, double x, double y) {
        if (returns == null || returns.isEmpty()) {
            throw new IllegalArgumentException("收益率列表不能为空");
        }

        int totalReturns = returns.size();
        int countInRange = 0;

        for (double ret : returns) {
            if (ret >= x && ret < y) {
                countInRange++;
            }
        }

        return (double) countInRange / totalReturns;
    }


    /**
     * 计算盈利概率
     *
     * @param returns 交易结果列表，正数表示盈利，负数表示亏损
     * @return 盈利概率
     * @throws IllegalArgumentException 如果交易结果列表为空，则抛出此异常
     */
    public static double calculateProfitProbability(List<Double> returns) {
        if (returns == null || returns.isEmpty()) {
            throw new IllegalArgumentException("交易结果列表不能为空");
        }

        int totalTrades = returns.size();
        int profitableTrades = 0;

        for (double ret : returns) {
            if (ret > 0) {
                profitableTrades++;
            }
        }

        return (double) profitableTrades / totalTrades;
    }


    /**
     * 计算收益回撤比
     *
     * @param initialNetWorth 初始净值
     * @param finalNetWorth   最终净值
     * @param netValues       净值列表，假设是一个时间序列的净值数据
     * @return 收益回撤比
     * @throws IllegalArgumentException 如果净值列表为空或包含的净值少于两个，则抛出此异常
     */
    public static double calculateReturnToDrawdownRatio(double initialNetWorth, double finalNetWorth, List<Double> netValues) {
        double cumulativeReturn = calculateCumulativeReturn(initialNetWorth, finalNetWorth);
        double maxDrawdown = calculateMaxDrawdown(netValues);
        if (maxDrawdown == 0) {
            throw new IllegalArgumentException("最大回撤不能为0");
        }
        return cumulativeReturn / maxDrawdown;
    }

    /**
     * 计算累计收益率
     *
     * @param initialNetWorth 初始净值
     * @param finalNetWorth   最终净值
     * @return 累计收益率
     */
    public static double calculateCumulativeReturn(double initialNetWorth, double finalNetWorth) {
        if (initialNetWorth == 0) {
            throw new IllegalArgumentException("初始净值不能为0");
        }
        return ((finalNetWorth - initialNetWorth) / initialNetWorth) * 100;
    }


    /**
     * 计算最大回撤的方法
     * 最大回撤是指在选定周期内，投资组合净值从最高点到最低点的回撤幅度
     *
     * @param netValues 净值列表，假设是一个时间序列的净值数据
     * @return 返回净值列表的最大回撤值
     * @throws IllegalArgumentException 如果净值列表为空或包含的净值少于两个，则抛出此异常
     */
    public static double calculateMaxDrawdown(List<Double> netValues) {
        // 检查输入的净值列表是否有效
        if (netValues == null || netValues.size() < 2) {
            throw new IllegalArgumentException("Net value list must contain at least two values.");
        }

        // 初始化最大回撤为0，峰值为第一个净值
        double maxDrawdown = 0.0;
        // 初始化峰值为第一个净值
        double peak = netValues.get(0);

        // 遍历净值列表，计算最大回撤
        for (double netValue : netValues) {
            // 更新峰值
            if (netValue > peak) {
                peak = netValue;
            }

            // 计算当前回撤
            double drawdown = (peak - netValue) / peak;

            // 更新最大回撤
            if (drawdown > maxDrawdown) {
                maxDrawdown = drawdown;
            }
        }

        return maxDrawdown;
    }


    // 净值涨幅 R=(X1-X2)/X2  初始净值-initialNetWorth、当前净值-currentNetWorth
    public static void netWorthGrowthCalculator(double initialNetWorth, double currentNetWorth) {
        //
        if (initialNetWorth == 0) {
            throw new IllegalArgumentException("旧净值不能为0");
        }
        // 计算涨幅
        double growth = ((currentNetWorth - initialNetWorth) / initialNetWorth) * 100;

        // 输出结果
        System.out.printf("涨幅为: %.2f%%\n", growth);
    }

    private static void testPow() {
        //计算数字的平方
        double num = 1.1d;
        double p = 2d;
        double pow = Math.pow(num, p);
        System.out.println("平方计算结果为：" + pow);
    }

    private static void testSqrt() {
        // 开根号
        double num = 12d;
        double result = Math.sqrt(num);
        System.out.println("开根号12：" + result);
    }


}
